package deanandjames.statistics;

import java.text.DecimalFormat;

public class Statistics {
	private static long MAX_STATS_INTERVAL = 1000000000L;
	// Number of frames with a delay of 0 ms before the animation thread yields
	// to other running threads.
	private static int NUM_FPS = 10;
	private double averageFPS = 0.0;
	private double averageUPS = 0.0;
	private final DecimalFormat df = new DecimalFormat("0.##");
	private final double fpsStore[];
	private long frameCount = 0;
	private long framesSkipped = 0L;
	private long gameStartTime;
	private long prevStatsTime;
	private long statsCount = 0;
	private long statsIntervalInNanoseconds = 0L;
	private int timeSpentInGame = 0; // in seconds

	private long totalElapsedTime = 0L;

	private long totalFramesSkipped = 0L;

	private final double upsStore[];

	// private final DecimalFormat timedf = new DecimalFormat("0.####"); // 4 dp
	public Statistics() {
		fpsStore = new double[NUM_FPS];
		upsStore = new double[NUM_FPS];
		for (int i = 0; i < NUM_FPS; i++) {
			fpsStore[i] = 0.0;
			upsStore[i] = 0.0;
		}
	}

	public void addSkippedFrames(int skips) {
		framesSkipped += skips;
	}

	public long getGameStartTime() {
		return gameStartTime;
	}

	public String getStats() {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append("Frame Count/Loss: " + frameCount + " / " + totalFramesSkipped);
		stringBuffer.append("Average FPS: " + df.format(averageFPS));
		stringBuffer.append("Average UPS: " + df.format(averageUPS));
		stringBuffer.append("Time Spent: " + timeSpentInGame + " secs");
		return stringBuffer.toString();
	}

	public String getStatsLine() {
		return "Average FPS/UPS: " + df.format(averageFPS) + ", " + df.format(averageUPS);
	}

	public String getTimeSpentLine() {
		return "Time Spent: " + timeSpentInGame + " secs";
	}

	public void setGameStartTime(long nanoTime) {
		gameStartTime = nanoTime;
		prevStatsTime = gameStartTime;

	}

	public void storeStats(long period) {
		/*
				 * The statistics: - the summed periods for all the iterations in this interval (period is the amount of time a single frame iteration should
				 * take), the actual elapsed time in this interval, the error between these two numbers; - the total frame count, which is the total number of
				 * calls to run(); - the frames skipped in this interval, the total number of frames skipped. A frame skip is a game update without a
				 * corresponding render; - the FPS (frames/sec) and UPS (updates/sec) for this interval, the average FPS & UPS over the last NUM_FPSs
				 * intervals. The data is collected every MAX_STATS_INTERVAL (1 sec).
				 */
		frameCount++;
		statsIntervalInNanoseconds += period;
		if (statsIntervalInNanoseconds >= MAX_STATS_INTERVAL) {
			// record stats every MAX_STATS_INTERVAL
			final long timeNow = System.nanoTime();
			timeSpentInGame = (int) ((timeNow - gameStartTime) / 1000000000L);
			// ns --> secs
			final long realElapsedTime = timeNow - prevStatsTime;
			// time since last stats collection
			totalElapsedTime += realElapsedTime;
			totalFramesSkipped += framesSkipped;
			// calculate the latest FPS and UPS
			double actualFPS = 0;
			double actualUPS = 0;
			if (totalElapsedTime > 0) {
				actualFPS = (double) frameCount / totalElapsedTime * 1000000000L;
				actualUPS = (double) (frameCount + totalFramesSkipped) / totalElapsedTime * 1000000000L;
			}
			update(actualFPS, actualUPS);

			/*
						 * System.out.println(timedf.format( (double) statsInterval/1000000000L) + " " + timedf.format((double) realElapsedTime/1000000000L) + "s " +
						 * df.format(timingError) + "% " + frameCount + "c " + framesSkipped + "/" + totalFramesSkipped + " skip; " + df.format(actualFPS) + " " +
						 * df.format(averageFPS) + " afps; " + df.format(actualUPS) + " " + df.format(averageUPS) + " aups" );
						 */
			framesSkipped = 0;
			prevStatsTime = timeNow;
			// reset
			statsIntervalInNanoseconds = 0L;
		}
	}

	public void update(double actualFPS, double actualUPS) {
		// store the latest FPS and UPS
		fpsStore[(int) statsCount % NUM_FPS] = actualFPS;
		upsStore[(int) statsCount % NUM_FPS] = actualUPS;
		statsCount = statsCount + 1;

		double totalFPS = 0.0; // total the stored FPSs and UPSs
		double totalUPS = 0.0;
		for (int i = 0; i < NUM_FPS; i++) {
			totalFPS += fpsStore[i];
			totalUPS += upsStore[i];
		}
		if (statsCount < NUM_FPS) {
			// obtain the average FPS and UPS
			averageFPS = totalFPS / statsCount;
			averageUPS = totalUPS / statsCount;
		} else {
			averageFPS = totalFPS / NUM_FPS;
			averageUPS = totalUPS / NUM_FPS;
		}
	}
}
